# 对象：python中所有的一切（数字、字符串、函数、模块）都是对象，并且都可以赋值给变量
# 4种内置的数据结构（用来保存对象的集合）：列表、元组、字典、集合  注：数据结构是python语言中的部分所以使用时不需要导入 直接作为代码
# 1.列表——有序的可变对象集合
# 理解：a 有序 就意味着列表中每个元素的位置是有顺序的 任何列表的顺序都是从左到右 从0开始计算元素  （列表相当于一个索引集合）
#      b 可变 就意味着列表是动态的，可以随时更改列表中的对象或删除或增加对象

# 2.元组——有序的不可变对象集合
# 理解：a 有序 同理列表的有序性 也使用索引值，从0开始
#      b 不可变  就意味着列表是一个常量，里面的元素不可以修改、增加、删除

# 3.字典——无序的键/值对集合
# a 字典里面可以包含多个键值对 而每个键都有一个与之关联的值 想象为一个两列多行的“表格”
# b 字典是无序且可变的
# c 字典内部可以任意增加或删除键值对

# 4.集合——无序的唯一对象集合
# a 用来保存相关对象的一个集合
# 集合内部不会有重复对象
# 集合是可变的 可以增加或删除对象
# 集合是无序的 集合中对象的顺序是无意义的

### 列表
# 列表是用[]包围   用,来分隔每个对象
# 按字面创建列表      字面列表是指一次性将对象赋给新列表（之后不再改动）
prices = []  # 将空列表赋值给变量prices
temps = [36.0,120.5,189.3,230.6,50.0]  # 这是一个浮点列表
word = ['hello','word']  # 这是一个单词列表 注意如果列表内是字符串的话一定要用引号包起来
# 列表可以存储混合类型的对象
car_deltais =['Toyota','RAV4',2.5,6087]
# 列表本身也是对象 所以可以列表包含多个列表
everything = [prices,temps,word,car_deltais]
odds_and_ends = [[1,2,3],['a','b','c'],['one','tow','three']]

# 查看一个对象是否在另外一个对象中用“in”
vowels = ['a','e','i','o','u']
word = "Milliways"
for letter in word:   # 取单词中的各个字母
    if letter in vowels:  # 单词的字母是否在列表里面
        print(letter)

### 列表中添加对象
# 1.attend  一次只能添加一个元素 ，每次添加的元素都在上一个的后面 所以用attend可以在列表末尾添加对象
# 通过列表中的元音字母来查询对象单词 ——查找到后 只列出唯一字母 不要重复
found = []  # 先给一个新变量赋予一个空列表
len(found)  # 再用len内置函数来检查列表中有多少个对象  len函数会报告一个对象的大小
found.append('a')   # 在空列表中添上一个对象             # 点记法：在列表名后面加上一个点号和方法调用  list.append(添加的对象)
found.append('e')
found.append('i')
found.append('o')  # 这里相当于在列表末尾添加“o”对象
print(found)  # ['a', 'e', 'i', 'o']

# 对象在列表里面用 in    不在就用 not in 注意使用not in时前提是你知道你所要添加的元素不再列表中 如果已经存在的元素再添加，则不会执行append
found = [1,2]
if 3 not in found:
    found.append(3)
    print(found)  # [1, 2, 3]

# 课本项目练习
vowels = ['a','e','i','o','u']  # 元音字母检索列表
word = "Milliways"  # 给变量word赋一个单词
found = []          # 添加一个空列表
for letter in word:  # 取出单词中的各个字母 （因为一次只能取出一个所以用for） 这里的letter是指每次for循环取出的对象赋给另外一个新变量letter（理解为，从袋子里拿糖果，一次拿一个，拿出来后给某个人 这里得到糖果的人就是letter，袋子和里面的糖果就是word列表）
    if letter in vowels:   # 判断单词的字母是否在元音字母列表中
        if letter not in found:   # 如果单词字母不在found空列表中 （前面赋给found的是空列表，所以可以执行append）
            found.append(letter)  # 就在found列表中添加单词的字母  所添加的字母是在元音字母表中
for vowel in found:    # 该循环是在上一个for循环结束后才开始运行的   命名新变量vowel 用for循环在found列表中取出字母
    print(vowel)       # 为什么取出的值就是i a？？？
# 运行结果为: i a
# 修改  让用户输入任意单词来查找其中的元音字母（产生交互用input）
vowels = ['a','e','i','o','u']
word = input("请提供一个单词供检索元音字母：")
found = []
for letter in word:
    if letter in vowels:
        if letter not in found:
            found.append(letter)
for vowel in found:
    print(vowel)

### 列表中删除对象
# 1.remove：取出一个对象值作为唯一参数
# 删除指定数据值的第一次出现 找到这个数据值后就删除包含这个值的对象，列表的大小会减小1。
# 如果所要删掉的值不在列表中 则结果会报错
# remove函数使用前提下是知道你所要删去的值
nums = [1,2,3,4,5]
nums.remove(4)  # 同理列表之后调用函数remove  list.remove(要删除的数据值即对象) 一次只能删一个
print(nums)

# 2.pop：取一个可选的索引值作为参数
# 根据所要删除的对象的索引值从列表中删除和返回一个对象   返回的对象会被回收 所以看不到
# 如果没有删除索引值的话 就会删去和返回列表中最后一个对象
# 如果列表为空或指定了一个不存在的索引值 则会报错
nums = [1,2,3,4,5]
nums.pop()  # 没有告知删除的索引值 则删去最后一个对象
print(nums)  # 结果为[1, 2, 3, 4]

nums = [1,2,3,4]
nums.pop(2)
print(nums)  # [1, 2, 4]


### 列表扩展（添加）列表对象
# 3.extend：取一个对象列表作为唯一参数
# 将多个列表合并到一个列表中
nums = [1,2]
nums.extend([3,4,5])
print(nums)  # [1, 2, 3, 4, 5]
# 也可以添加空列表 但没什么效果
nums = [3,4]
nums.extend([])
print(nums)  # [3,4]


### 列表中插入一个对象
# 4.insert: 取一个索引值和一个对象作为参数
# 将某个对象插入到列表中指定的索引值的前面  可以实现将某个对象插入到列表的开头，但是对象插入到列表的末尾用的是append
nums = [2,5,7]
nums.insert(0,1)  # list.insert(a,b)  a:要插入的对象的索引在a这个位置的前面  b：要插入的具体对象
print(nums)  # [1, 2, 5, 7]

# 拓：将某个对象插入任意槽中
nums = [1,2,4,5]
nums.insert(2,'three')
print(nums)  # [1, 2, 'three', 4, 5]

# 课本练习项目
# 将"Don't panic" 转换为 on tap
phrase = "Don't panic!"
plist = list(phrase)   # 这两行代码将字符串转换为列表
print(phrase)    # Don't panic!      注意词组中的空格也是字符串的一部分 不能省!!!
print(plist)     # ['D', 'o', 'n', "'", 't', ' ', 'p', 'a', 'n', 'i', 'c', '!']对应的索引值为[0,1,2,3,4,5,6,7,8,9,10,11]

for i in range(4):  # 操作要多次才能完成所以用for循环更加方便
    plist.pop()     # 这里是用pop+for循环不断的删去列表中最后一个索引值（删去第一个最后值之后 后面会向前递补，所以要循环4次，这样就可以删去最末尾的4个对象）
plist.pop(0)        # 接着删去不想要的字母 这里删去第一个字母“D”
plist.remove("'")   # 删去列表中的单引号   此时列表为[o,n,t, ,p,a]
plist.extend([plist.pop(),plist.pop()])  # 先调用pop（从左到右），第一个pop删去“a”，第二个删去“p” ，此时列表为[o,p,t, ] 现在又调用extend将刚刚被弹出的对象重新添加到列表末位，按刚刚删除的顺序来添加 此时列表为[o,p,t, ,a,p]
plist.insert(2,plist.pop(3))    # 要将索引2位置与索引3位置调换，用insert  ； 先用pop取出空格字符串，此时列表[o,p,t,a,p],接着再插入空格 在索引2处即t的前面插入空格。

new_phrase = ''.join(plist)   # 这行代码将列表转换为字符串  一般用  变量 = ''.join(列表)
print(plist)        # 原来的列表变为了:['o', 'n', ' ', 't', 'a', 'p'] 说明使用列表方法后会改变原列表
print(new_phrase)    # on tap


### 列表复制(复制数据结构)
# 列表复制不能直接将一个列表赋值给另外一个新变量使新变量变为一个新列表  这样会导致改变新列表内的对象时，原来的列表也会改动
# 用copy方法
first = [1,2,3]
second = first.copy()
print(second)  # [1, 2, 3]
print(first)   # [1, 2, 3]

second.append(4)
print(second)  # [1, 2, 3, 4]
print(first)   # [1, 2, 3]


### 列表的索引和切片
# 索引值有正值和负值 正负代表不同的方向   正向索引从左到右从0开始  负向索引从右到左从-1开始
# [A,B,C,D,E,F,G]  对应的正向索引值为：0，1，2，3，4，5，6   对应负向索引值为：-7，-6，-5，-4，-3，-2，-1  列表的索引值个数无论正负都相等，从数学的角度上看，右边的永远比左边大
# 单索引取值
list = ['a','b','c','d','e','f']
print(list[3])  # d
print(list[-3])  # d
# 取0时可以得到列表的开头，取-1时可以得到列表末尾
# 列表的切片
# list[开始:结束:步长]     # 类比范围取值   开始索引值指从哪里开始 结束索引值指到哪里结束 步长值是指从开始索引位置到结束索引位置之间每一步的大小或者说相当于间隔多少取一次对象
# 如果没有给开始、结束、步长指定索引值，则默认为：list[0(即列表的开头)，列表的最大值（即列表的末位），1（默认正向步长）]
# 注意 列表的结束索引值上的对象是取不到的 [即括号里包前不报后]  若要取到末尾值的对象就不给结束任何索引值[6::2] 这样就默认取到列表最末位
saying = "Don't panic!"
plist = list(saying)
print(plist)   # ['D', 'o', 'n', "'", 't', ' ', 'p', 'a', 'n', 'i', 'c', '!']
print(plist[0:10:3])  # ['D', "'", 'p', 'i']
print(plist[3:])      # ["'", 't', ' ', 'p', 'a', 'n', 'i', 'c', '!']
print(plist[:10])     # ['D', 'o', 'n', "'", 't', ' ', 'p', 'a', 'n', 'i']  索引10是c取不到
print(plist[::2])     # ['D', 'n', 't', 'p', 'n', 'c']
# 切片取出列表
book = "the hitchhiker's guide to the galaxy"
booklist = list(book)
print(booklist)  # ['t', 'h', 'e', ' ', 'H', 'i', 't', 'c', 'h', 'h', 'i', 'k', 'e', 'r', "'", 's', ' ', 'g', 'u', 'i', 'd', 'e', ' ', 't', 'o', ' ', 't', 'h', 'e', ' ', 'g', 'a', 'l', 'a', 'x', 'y']
print(booklist[0:4])  # ['t', 'h', 'e', ' ']
backwards = ''.join(booklist[0:3])  # 将切片取出的字母转化为字符串
backwards_ = ''.join(booklist[-6:])      # 注意当开始索引值或者结束索引值用的是负值时 就要从列表右边往左边开始计数 这只是针对于你要找索引值时的做法  这里[-6:]则为从-6的索引对象开始向右取对象 可以想象为这里的步长默认为1
print(backwards)    # the
print(backwards_)   # galaxy
# 列表的步长
backward = booklist[::-1]  # 这里选取所有的字母所以不给开始和结束赋索引值，  步长为反向取值
pa = ''.join(backward)
print(pa)  #  yxalag eht ot ediug s'rekihhctiH eht  因为步长为反向 所以从列表的末尾开始取对象 又因为默认开始和结束所以所有的对象都取到了

every_other = booklist[::2]   #这里也是选取所有的对象 但步长为正向且每隔2个字母取值
pb = ''.join(every_other)
print(pb)   # teHthie' ud oteglx

every_other = booklist[4:14]    # 这里采用任意的开始和结束索引值 步长默认为1
pc = ''.join(every_other)
print(pc)  # Hitchhiker

every_other = booklist[13:3:-1]   # 要使反向取值步长为1，一定要给步长定值为-1
pd = ''.join(every_other)
print(pd)  # rekihhctiH

every_other = booklist[-8:-11:-1]
pe = ''.join(every_other)
print(pe)  # eht
# 方便理解：['t', 'h', 'e', ' ', 'H', 'i', 't', 'c', 'h', 'h', 'i', 'k', 'e', 'r', "'", 's', ' ', 'g', 'u', 'i', 'd', 'e', ' ', 't', 'o', ' ', 't', 'h', 'e', ' ', 'g', 'a', 'l', 'a', 'x', 'y']
every_other = booklist[-8:-11:]  # 在列表切片的开始和结束部分[t,h,e]
pf = ''.join(every_other)
print(pf)  # 这样写python是无法执行的 因为[-8:-11:1] 开始和结束都是反向 不可能再正向取值了 -8的对象为e 它的正向取应该为[ ,g,a,······]而又限定了结束值在-11（取出的对象为t）t在e的左边 所以这是根本不可能的

every_other = booklist[-8:-4:2]  # 在列表切片的开始和结束部分[e, ,g,a]范围里取步长为2
pg = ''.join(every_other)
print(pg)  # eg

# 课本项目练习
# 现在用列表切片的方法来实现对“Don't panic!”切出“on tap”
phrase = "Don't panic!"
plist = list(phrase)
print(phrase)
print(plist)
new_phrase = ''.join(plist[1:3])  # 从字母表中取出 on
new_phrase = new_phrase+ ''.join([plist[5],plist[4],plist[7],plist[6]])  # 再从列表中切出目标字母 最后转换为字符串（就是把单个对象组合在一起）
print(plist)       # 补充：将多个列表转换为字符串时用 ''.join(['ab','cd','ef']) 括号内要用[]把多个切片对象包起来并用,间隔 这些切出来的字母用[]包起来就组成一个列表
print(new_phrase)  # on tap        *（接上个注释） 如果只有单个列表则不用加[]  因为join本身是针对于列表转换为字符串 所以（）内必须为列表 当只有单个列表时 可以直接写为''.join（列表变量名） 多个单独对象变为字符串时就要，先将这些对象用[]包起来变为一个列表再转化为字符串

# 比较两种方法取出on tap
# 1.切片取值法不会改变原来的列表  2.列表取法会改变原来的列表 3.哪种方法更好取决于你要做什么

# for循环和列表
paranoid_android = "Marvin"
letters = list(paranoid_android)
for char in letters:   # 这里的新变量char:指每次循环时 for循环出的对象赋给新变量char
    print('\t',char)  # 用了for列表会分行打印 且列表前面有一个制表符(看不见用鼠标可以感受到 一个制表符就是键盘上的一个tab键宽)
# 	 M
# 	 a
# 	 r
# 	 v
# 	 i
# 	 n

# for循环和切片
paranoid_android = "Marvin,the Paranoid Android"
letters = list(paranoid_android)
for char in letters[:6]:
    print('\t',char)
print()
for char in letters[-7:]:
    print('\t'*2,char)   # 插入制表符也是为了美观性，这里的循环结果前面有2个制表符
print()
for char in letters[12:20]:
    print('\t'*3,char)   # "*"python中的乘法操作符，用法： \t（制表符）*制表符出现的次数
# 	 M
# 	 a
# 	 r
# 	 v
# 	 i
# 	 n
#
# 		 A
# 		 n
# 		 d
# 		 r
# 		 o
# 		 i
# 		 d
#
# 			 a
# 			 r
# 			 a
# 			 n
# 			 o
# 			 i
# 			 d